package com.cskaoyan.javase.oop3._2inner._1member._1basic;

/**
 * 在具体详细了解成员内部类的访问特点之前,要先明确以下两个原则:
 * 1.成员内部类成为外围类的"成员",它们之间的访问是不受权限限制的,即便是私有也能够随意访问.
 * 2.如何理解"成员内部类对象依赖于外围类对象而存在呢?"
 * 成员内部类对象会持有外围类对象的引用作为一个成员变量
 * 思考:
 *      a.在成员内部类的成员方法中,存在几个对象呢?
 *      因为是成员方法,首先肯定存在自身类对象(当前对象),用this指向
 *      而要想得到自身成员内部类对象,首先需要创建外围类对象,并且持有外围类对象的引用
 *      所以既然成员内部类对象存在了,那么外围类对象也一定存在
 *      所以这里存在两个对象:
 *          1.自身当前对象,用this指向
 *          2.外围类对象,用外围类类名.this指向
 *
 *      b.在外围类的成员方法中,存在几个对象呢?
 *          首先肯定存在一个对象
 *          this指向的外围类自身对象(当前对象)
 *          要记住是成员内部类对象依赖于外围类对象
 *          反过来没有依赖关系
 *          所以这里不存在成员内部类对象
 *          所以只有一个对象
 *
 *      c.在外围类的静态成员方法中,存在几个对象呢?
 *          没有任何对象
 *
 * 成员内部类的访问特点，主要从以下几个角度研究：
 * 1.成员内部类内部访问外围类
 *      要想访问外围类成员,首先需要外围类的对象
 *      访问的位置: 成员方法和构造器(一样的概念)
 *      这个访问为孩子已经存在两个对象了:
 *      1.自身当前对象,用this指向
 *      2.外围类对象,用外围类类名.this指向
 *      现在访问外围类成员,由于对象已存在,所以可以直接访问.
 *
 *      如果外围类和成员内部类出现了同名的成员变量,如何区分呢?
 *      用this,外围类类名.this指向不同对象,以示区分.
 *
 *       如果外围类和成员内部类出现了同名的全局常量,如何区分呢?
 *       用类名区分就可以了.
 *
 * 2.外围类访问成员内部类成员
 *      要想访问成员内部类成员,首先需要它的对象
 *      访问位置: 成员方法以及静态方法
 *
 *      a.成员方法中的访问
 *      这里仅在存在自身外围类对象,只存在一个对象
 *      用this指向
 *      现在需要创建成员内部类对象
 *      如何创建呢?
 *      既然已经存在外围类对象,所以可以直接创建成员内部类对象.
 *      语法:
 *           直接new
 *           this.new Inner();
 *      这时如果出现同名,就不需要区分了.
 *
 *      b.静态成员方法中的访问
 *          由于没有任何对象
 *           现在需要创建成员内部类对象
 *          如何创建呢?
 *          要先创建外围类对象,然后在外围类对象的基础上创建成员内部类对象.
 *          语法:
 *          Inner i = new EnclosedClazz().new Inner();
 *          这时访问谁的成员就用谁的引用
 *          不存在同名区分了.
 *
 * 3.外部类访问成员内部类成员
 *      绝大多数情况下,既然定义内部类,都是为了隐藏,封装类
 *      所以成员内部类的访问权限修饰符往往都是private
 *      这样在外部是不可能访问它的
 *      但如果没有私有化成员内部类,且在外部有权限,是可以访问成员内部类成员的
 *
 *      这时的访问要受权限限制
 *      要想在外部类中访问成员内部类成员,需要突破以下权限:
 *          a.首先需要外围类权限
 *          b.其次需要成员内部类权限
 *          c.最后还需要成员内部类的成员权限
 *     以上权限缺一不可.
 *     在外部类中,无论是成员方法还是静态方法,访问是没有区别的
 *     都要先创建外围类对象,再创建成员内部类对象
 *     语法:
 *      EnclosedClazz.Inner inner = new EnclosedClazz().new Inner();
 *      注意,引用的数据类型,需要指出成员内部类属于哪个外围类.
 *
 * 4.成员内部类访问外部类成员
 *      能访问,受权限限制,需要创建对象等等.
 *      和普通类之间的访问,没有区别.
 *
 * @since 15:45
 * @author wuguidong@cskaoyan.onaliyun.com
 */
public class Demo {
    public void test() {

    }

    public static void main(String[] args) {
        EnclosedClazz ec = new EnclosedClazz();
        EnclosedClazz.Inner i = ec.new Inner();
        System.out.println(i.a);
        // System.out.println(i.b);

        EnclosedClazz.Inner i2 = new EnclosedClazz().new Inner();
    }
}

class EnclosedClazz {
    int a;
    private int b = 10;
    static int c = 20;
    static final int D = 30;

    // 外围类的成员方法
    public void test() {
        // 直接创建成员内部类对象
        Inner i = new Inner();
        System.out.println(i.a);
        System.out.println(i.b);

        // 这里能够直接new的原因是存在this,如果写完整
        // 这叫做在外围类对象的基础上创建
        Inner i2 = this.new Inner();
    }

    // 外围类的静态成员方法
    public static void testStatic() {
        // 先创建外围类对象
        EnclosedClazz ec = new EnclosedClazz();
        // 在外围类对象的基础上创建成员内部类对象
        Inner i = ec.new Inner();
        // 用链式调用,以上二合一
        Inner i2 = new EnclosedClazz().new Inner();
    }

    class Inner {
        int a;
        private int b = 100;
        // static int c = 20;
        static final int D = 300;

        // 成员内部类的成员方法
        void testInner(int b) {
            System.out.println(b);
            System.out.println(this.b);
            System.out.println(EnclosedClazz.this.b);
            System.out.println(EnclosedClazz.this.a);
            System.out.println(a);
            System.out.println(EnclosedClazz.c);
            System.out.println(EnclosedClazz.D);
            System.out.println(Inner.D);
        }

        public Inner() {
            System.out.println(this);
            System.out.println(EnclosedClazz.this);
        }
    }
}

// 补全程序，使得可以输出三个num
class Outer {
    public int num = 10;
    class Inner {
        public int num = 20;
        public void show() {
            int num = 30;
            System.out.println(this.num);
            System.out.println(num);
            System.out.println(Outer.this.num);
        }
    }
}